package com.yahoo.astra.fl.charts.axes
{
	import com.yahoo.astra.fl.charts.series.ISeries;

	/**
	 * An axis type representing a set of categories.
	 * 
	 * @author Josh Tynjala
	 */
	public class CategoryAxis extends BaseAxis implements IAxis, IClusteringAxis
	{
		
	//--------------------------------------
	//  Constructor
	//--------------------------------------
	
		/**
		 * Constructor.
		 */
		public function CategoryAxis()
		{
		}
		
	//--------------------------------------
	//  Properties
	//--------------------------------------
		
		/**
		 * @private
		 * Used to determine the position of an item based on a value.
		 */
		protected var categorySize:Number = 0;
		
		/**
		 * @private
		 * Storage for the categoryNames property.
		 */
		private var _categoryNames:Array = [];
		
		/**
		 * @private
		 * Indicates whether the category labels are user-defined or generated by the axis.
		 */
		private var _categoryNamesSetByUser:Boolean = false;
		
		/**
		 * The category labels to display along the axis.
		 */
		public function get categoryNames():Array
		{
			return this._categoryNames;
		}
		
		/**
		 * @private
		 */
		public function set categoryNames(value:Array):void
		{
			this._categoryNamesSetByUser = value != null && value.length > 0;
			if(!this._categoryNamesSetByUser)
			{
				this._categoryNames = [];
			}
			else
			{
				//ensure that all category names are strings
				var names:Array = [];
				for(var i:int = 0; i < value.length; i++)
				{
					names.push(value[i].toString());
				}
				this._categoryNames = names;
			}
		}
		
		/**
		 * @inheritDoc
		 */
		public function get clusterCount():int
		{
			return this.categoryNames.length;
		}
		
	//--------------------------------------
	//  Public Methods
	//--------------------------------------
		
		/**
		 * @inheritDoc
		 */
		public function valueToLocal(value:Object):Number
		{
			if(value === null)
			{
				return NaN;
			}
			
			var index:int = this.categoryNames.indexOf(value.toString());
			if(index >= 0)
			{
				var position:int = this.categorySize * index + (this.categorySize / 2);
				if(this.reverse)
				{
					position = this.renderer.length - position;
				}
				return position;
			}
			return NaN;
		}
	
		/**
		 * @inheritDoc
		 */
		public function updateScale(data:Array):void
		{
			if(!this._categoryNamesSetByUser)
			{
				this.autoDetectCategories(data);
			}
			this.calculateCategorySize();
			
			var ticks:Array = [];
			var categoryCount:int = this.categoryNames.length;
			for(var i:int = 0; i < categoryCount; i++)
			{
				var category:String = this.categoryNames[i];
				var position:Number = this.valueToLocal(category);
				var label:String = this.valueToLabel(category);
				var axisData:AxisData = new AxisData(position, category, label);
				ticks.push(axisData);
			}
			this.renderer.ticks = ticks;
			this.renderer.minorTicks = [];
		}
	
	//--------------------------------------
	//  Public Methods
	//--------------------------------------
	
		/**
		 * @private
		 * Update the labels by adding or removing some, setting the text, etc.
		 */
		private function autoDetectCategories(data:Array):void
		{
			var uniqueCategoryNames:Array = [];
			var seriesCount:int = data.length;
			for(var i:int = 0; i < seriesCount; i++)
			{
				var series:ISeries = data[i] as ISeries;
				if(!series)
				{
					continue;
				}
				
				var seriesLength:int = series.length;
				for(var j:int = 0; j < seriesLength; j++)
				{
					var category:Object = this.chart.itemToAxisValue(series, j, this);
					
					//names must be unique
					if(uniqueCategoryNames.indexOf(category) < 0)
					{
						uniqueCategoryNames.push(category);
					}
				}
			}
			this.categoryNames = uniqueCategoryNames.concat();
		}
		
		/**
		 * @private
		 * Determines the amount of space provided to each category.
		 */
		private function calculateCategorySize():void
		{
			var categoryCount:int = this.categoryNames.length;
			this.categorySize = this.renderer.length;
			if(categoryCount > 0)
			{
				this.categorySize /= categoryCount;
			}
		}
	}
}